package org.concepts.java.core.concurrency.executor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.concepts.java.utils.DataUtils;
import org.junit.Test;

/**
 * An {@link Executor} is an interface that defines just one method
 * {@link Executor#execute(Runnable)}. The goal is to decouple the task
 * (Runnable) for how it's executed (user thread, new thread, from a pool, now
 * or later, more than one time etc..). See Javadoc of {@link Executor} for
 * examples of writing some simple executors.
 * <p/>
 * {@link ExecutorService} extends executor. The service can be shutdown. User
 * can submit a {@link Callable} or a {@link Runnable}. On submission a
 * {@link FutureTask} is returned. Future task can be used to query the
 * execution state. User can also block on {@link FutureTask#get()} till the
 * execution of the task completes. Finally, executor service can be used for
 * batch submission of tasks.
 * <p/>
 * {@link ScheduledExecutorService} add the capability to schedule the execution
 * of tasks after after a given delay. User can also use it to repeat execution
 * of tasks. Use this service in place of {@link Timer}.
 * <p/>
 * {@link Executors} provides factors methods for {@link Executor} and
 * {@link ExecutorService}, along with other classes in java.util.concurrent
 * package. Executors methods can be use to access some executor services that
 * JDK library provides.
 * <p/>
 * Users can create their own advanced executor services by using
 * {@link ThreadPoolExecutor} directly.
 * 
 * 
 * @author vvvv
 * 
 */
public class TestExecutorServices {

  /**
   * The tasks are executed in the same thread one after the other. Think of it
   * like a thread pool of 1, with the benefit that if a given thread
   * terminates, then a new one is created for the next request.
   * 
   */
  @Test
  public void testSingleThreadExecutor() throws InterruptedException,
      ExecutionException {
    runExecutorSvc(Executors.newSingleThreadExecutor(), getSortTasks(5, 100,
        1000));
  }

  /**
   * The tasks are executed in a fixed thread pool. The threads are reused.
   * <p/>
   * Fixed thread pool is useful for systems that can have long running jobs .
   * You can also provide a way for user to configure the size of the thread
   * pool.
   */
  @Test
  public void testFixedThreadPool() throws InterruptedException,
      ExecutionException {
    runExecutorSvc(Executors.newFixedThreadPool(2), getSortTasks(5, 100, 1000));
  }

  /**
   * New threads are created and added to the pool, if there isn't one available
   * to service the request. Idle threads are terminated after 60 seconds.
   * <p/>
   * The pool is good for quick running tasks. Long running tasks will result in
   * lots of threads getting created if there's a burst of new tasks. Thus this
   * kind of executor is not good for heavily loaded servers. The plus side is
   * that there's no end user configuration. Use in applications where the
   * behavior of number of task requests is well defined.
   */
  @Test
  public void testCachedThreadPool() throws InterruptedException,
      ExecutionException {
    runExecutorSvc(Executors.newCachedThreadPool(), getSortTasks(50, 100, 1000));
  }

  /**
   * Scheduled executor provides ability to run tasks at a later time. It's
   * a replacement for {@link java.util.Timer}.  
   */
  @Test
  public void testScheduledThreadPool() throws InterruptedException,
      ExecutionException {
    ScheduledExecutorService schExecutorSvc = Executors
        .newScheduledThreadPool(2);
    Collection<Callable<int[]>> sortTasks = getSortTasks(5, 5, 100);
    for (Callable<int[]> task : sortTasks) {
      System.out.println("scheuling at: " + new Date().getTime());
      ScheduledFuture<int[]> futureTask = schExecutorSvc.schedule(task, 1,
          TimeUnit.SECONDS);
      futureTask.get();
      System.out.println("finished at: " + new Date().getTime());
    }
  }

  /**
   * Runs the tasks in the executor service
   * 
   * @param executorService
   * @param tasks
   * @throws InterruptedException
   * @throws ExecutionException
   */
  private void runExecutorSvc(ExecutorService executorService,
      Collection<Callable<int[]>> tasks) throws InterruptedException,
      ExecutionException {
    List<Future<int[]>> futureTasks = executorService.invokeAll(tasks);
    for (Future<int[]> task : futureTasks) {
      task.get();
    }
  }

  /**
   * Returns a list of callable tasks that can sort an integer array. For each
   * task the integer array is randomly generated. sortSize specified the size
   * of the array. Each integer in the array has a value <= sortMaxValue.
   * 
   * @param count
   *          >=1. Number of tasks.
   * @param sortSize
   *          >=1. The number of integers that a given task has to sort.
   * @param sortMaxValue
   *          >=1. The maximum value of an integer in a given sort array.
   * @return
   */
  private Collection<Callable<int[]>> getSortTasks(int count, int sortSize,
      int sortMaxValue) {
    Collection<Callable<int[]>> tasks = new ArrayList<Callable<int[]>>();
    for (int ctr = 0; ctr < count; ctr++) {
      tasks.add(new SortNumberCallable(DataUtils.randomInts(sortSize,
          sortMaxValue)));
    }
    return tasks;
  }

}
